package com.can.single;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

/**
 * 懒汉式  单例模式
 */
public class LazyMan {

    private static boolean can = false;
    private LazyMan(){
        synchronized (LazyMan.class){
            if(can==false){
                can = true;
            }else{
                throw new RuntimeException("不要试图反射破坏异常");
            }

        }
        System.out.println(Thread.currentThread().getName()+" OK");
    }

    private volatile static LazyMan lazyMan;

    //双重检测锁模式的 懒汉式单例 DCL懒汉式
    public static LazyMan getInstance(){
        if(lazyMan==null){
            synchronized (LazyMan.class){
                if(lazyMan==null){
                    lazyMan = new LazyMan();    //不是原子性操作
                    /**
                     * 1.分配内存空间
                     * 2.执行构造方法，初始化对象
                     * 3.把这个对象指向这个空间
                     * 这三个可能指令重排，导致其他线程不知道对象初始化完成
                     */
                }
            }
        }

        return lazyMan; //此时LazyMan还没有构造
    }

    //单线程下确实OK
    //多线程并发会有问题
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {

//        for (int i = 0; i < 10000; i++) {
//            new Thread(()->{
//                LazyMan.getInstance();
//            }).start();
//        }

        //反射破坏
//        LazyMan instance = LazyMan.getInstance();
        Field can = LazyMan.class.getDeclaredField("can");
        can.setAccessible(true); //破坏私有权限
        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);//无视私有构造器
        LazyMan instance = declaredConstructor.newInstance();//两个对象都用反射创建 ，那么还是会有问题 //用标志位解决这个问题
        can.set(instance,false);            //但是通过反射，强行改变标识符，还是会有问题
        LazyMan instance2 = declaredConstructor.newInstance();
        System.out.println(instance==instance2);
        System.out.println(instance);
        System.out.println(instance2);
    }
}
